#include "fs.h"
#include "string.h"
#include "common.h"

static uint8_t zero[BLOCK_SIZE]; // always zero
static uint8_t sup[SECTOR_SIZE];  // super block buffer
static super_block_t *super_block = NULL;

static fd_t fds[NUM_FD];
static inode_entry_t *current_dir_entry;

uint8_t *blkmap;
uint8_t *inodemap;
inode_entry_t *inode;

dir_entry_t dbuf[BLOCK_SIZE/DENTRY_SIZE];
uint8_t fbuf[BLOCK_SIZE];
uint32_t addr_buf1[BLOCK_SIZE/4];
uint32_t addr_buf2[BLOCK_SIZE/4];

void imap_write(uint32_t inode_id)
{
    int waddr = 7 - inode_id % 8;
    inodemap[inode_id/8] |= (1 << waddr);
    sdwrite((char *)inodemap, SD_INODEMAP_ADDR, 0x200);
}
void bmap_write(uint32_t block_id)
{
    int waddr = 7 - block_id % 8;
    blkmap[block_id/8] |= (1 << waddr);
    sdwrite((char *)blkmap, SD_BLKMAP_ADDR, 0x4000);
}
void i_block_write(uint32_t inode_id)
{
    sdwrite((char *)(INODE_ADDR + BLOCK_SIZE*(inode_id/64)),  SD_INODE_ADDR + BLOCK_SIZE*(inode_id/64), BLOCK_SIZE);          
}
uint32_t get_free_block_id()
{
    int i,j;
    for(i = 0; i < BMAP_OFFSET; i++)
    {
        for(j = 0; j < 8; j++)
        {
           if(!(blkmap[i] &  (0x80 >>j)))
                return 8*i+j;
        }
    }
    return -1;
}
uint32_t get_free_inode_id()
{
    int i,j;
    for(i = 0; i < IMAP_OFFSET; i++)
    {
        for(j = 0; j < 8; j++)
        {
            if(!(inodemap[i] &  (0x80 >>j)))
                return 8 * i +j;
        }
    }
    return -1;
}

int alloc_free_block()
{
    uint32_t free_block_id = get_free_block_id();
    bmap_write(free_block_id);
    sdwrite(zero,SD_DATABLK_ADDR + free_block_id * BLOCK_SIZE,BLOCK_SIZE);
    return SD_DATABLK_ADDR + free_block_id * BLOCK_SIZE;
}

int alloc_free_inode()
{
    uint32_t free_inode_id = get_free_inode_id();
    imap_write(free_inode_id);
    return free_inode_id;
}
int alloc_free_fd()
{
    int i;
    for(i = 0;i < NUM_FD ;i++)
    {
        if(fds[i].inum==0)
            return i;
    }
    return -1;
}

void free_datablock(uint32_t block_addr)
{           
    uint32_t block_id = (block_addr-DATABLK_ADDR)/BLOCK_SIZE;
    int waddr = 7 - block_id % 8;
    blkmap[block_id/8] &= ~(1 << waddr);
    sdwrite((char *)blkmap, SD_BLKMAP_ADDR, BMAP_OFFSET);
}

void free_inode(uint32_t inode_id)
{                 
    int i;
    for(i = 0; i < 9; i ++)
        if(inode[inode_id].direct[i])
            free_datablock(inode[inode_id].direct[i]);
    int waddr = 7 - inode_id % 8;
    inodemap[inode_id/8] &= (~(1 << waddr));
    sdwrite((char *)inodemap, SD_INODEMAP_ADDR, IMAP_OFFSET);
}

void init_entry(uint32_t block_addr, uint32_t new_id, uint32_t parent_id)
{
    bzero(dbuf, sizeof(dbuf));
    strcpy((char *)dbuf[0].name, (char *)".");
    dbuf[0].type = 3;
    dbuf[0].inode_id = new_id;
    strcpy((char *)dbuf[1].name, (char *)"..");
    dbuf[1].type = 4;
    dbuf[1].inode_id = parent_id;
    sdwrite((char *)(dbuf), block_addr, BLOCK_SIZE);  
    sdread((char *)(dbuf), block_addr, BLOCK_SIZE);    
}
void get_first_path(char* first, char *dir)
{
    int i = 0;
    int j = 0;
    while(i<strlen(dir) && dir[i]!='/')
    {
        first[j]=dir[i];
        j ++;
        i ++;
    }
    first[j]='\0';
}

void get_new_path(char *new_path, char *dir)
{
    int i = 0;
    int j = 0;
    for(;i<strlen(dir)&&dir[i]!='/';i++);
    i++;
    while(i<strlen(dir))
    {
        new_path[j]=dir[i];
        j ++;
        i ++;
    }
    new_path[j]='\0';
}
void get_last_path(char *last_path, char *dir)
{
    int i = strlen(dir);
    int j = 0;
    for(i = strlen(dir);dir[i]!='/' && i >= 0;i--);
    i++;
    while(i<strlen(dir))
    {
        last_path[j]=dir[i];
        j ++;
        i ++;
    }
    last_path[j]='\0';
}
void get_search_path(char *search_path, char *dir)
{
    int i = strlen(dir);
    int j = 0;
    for(i = strlen(dir);dir[i]!='/' && i >= 0;i--);
    int k = i;
    i = 0;
    while(i < k)
    {
        search_path[j]=dir[i];
        j ++;
        i ++;
    }
    search_path[j]='\0';
}

int find_path(char * dir)
{
    
    char first[30];
    char new_path[30];
    int flag = 0;


    if(dir[0] == '/')
    {
        current_dir_entry = &inode[0];
        if(strlen(dir) == 1)
            return 1;
        dir ++;
    }
    
    strcpy(new_path,dir);
    get_first_path(first,new_path);
    if(first[0] == '\0')
        return 0;
    while(new_path[0]!='\0')
    {
        int j = 0;
        int k = 0;
        int dir_n = 0;
        sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);

        get_first_path(first,new_path);

        while(dir_n<current_dir_entry->son_num)
        {
            if(k ==128)
            {
                j ++;
                k = 0;
                sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
            }
            if(!strcmp((char *)dbuf[k].name,first))
            {
                current_dir_entry = &inode[dbuf[k].inode_id];
                get_new_path(new_path,new_path);
                flag = 1;
                if(new_path[0]=='\0')
                    return 1;
                break;
            }
            if(dbuf[k].name[0]!='\0') 
                dir_n++;
            k ++ ;
        }
        if(flag == 0)
            return 0;
        else 
            flag = 0;
    }
}

void do_mkfs()
{
    kprintf("[FS] Start initialize filesystem!          \n");
    kprintf("[FS] Setting superblock...                 \n");
    bzero((char *)super_block, SUPERBLK_OFFSET);

	super_block->magic_num=KFS_MAGIC;
    
    super_block->fs_sz = FS_SIZE;               

    super_block->blockmap_addr =BLKMAP_ADDR;
    super_block->inodemap_addr=INODEMAP_ADDR;

    super_block->inodes_addr=INODE_ADDR;
    super_block->inodes_num=512;              

    super_block->datablock_addr=DATABLK_ADDR;
    super_block->datablock_num=1048001;

    sdwrite((char *)super_block, SD_SUPERBLK_ADDR, SUPERBLK_OFFSET);

    kprintf("magic : %x                                        \n",super_block->magic_num);
    kprintf("num sector : %d ,start_sector : %d                \n",super_block->fs_sz/SECTOR_SIZE,OFFSET_FS/SECTOR_SIZE);
    kprintf("block map offset : %d (%d)                        \n",SD_BLKMAP_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE,BMAP_OFFSET/SECTOR_SIZE);
    kprintf("inode map offset : %d (%d)                        \n",SD_INODEMAP_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE,IMAP_OFFSET/SECTOR_SIZE);
    kprintf("inode offset : %d (%d)                            \n",SD_INODE_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE,super_block->inodes_num);
    kprintf("data offset : %d (%d)                             \n",SD_DATABLK_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE,super_block->datablock_num);
    kprintf("inode entry size : 64B, dir entry size : 32B      \n");
    
    kprintf("[FS] Setting inode-map...                 \n");
    inodemap = (uint8_t *)INODEMAP_ADDR;
    bzero((void *)inodemap, IMAP_OFFSET);
    sdwrite((char *)inodemap,SD_INODEMAP_ADDR, IMAP_OFFSET);
    

    kprintf("[FS] Setting block-map...                 \n");
    blkmap = (uint8_t *)BLKMAP_ADDR;
    bzero((void *)blkmap, BMAP_OFFSET);
    sdwrite((char *)blkmap,SD_BLKMAP_ADDR, BMAP_OFFSET);

    kprintf("[FS] Setting inode...                     \n");
    inode = (inode_entry_t *)INODE_ADDR;
    bzero((void *)inode, INODE_OFFSET);
    sdwrite((char *)inode,SD_INODE_ADDR, INODE_OFFSET);

//root inode
    imap_write(0);
    inode[0].num = 0;
    inode[0].i_mode = 1;
    inode[0].file_mode = O_RDWR;
    inode[0].son_num =2;
    inode[0].filesz =0;
    inode[0].ref = 2;
    inode->direct[0] = alloc_free_block(); 
    int i;
    for(i=1;i<9;i++)
        inode->direct[i] =0;
    i_block_write(0);
    init_entry(inode[0].direct[0],0,0);
    kprintf("[FS] Initialize filesystem finished!       \n");

    current_dir_entry = inode;
}
void statfs()
{
    int i, j;
    uint32_t used_block = 0, used_inodes = 0;
    for(i = 0; i < BMAP_OFFSET; i++)
        for(j = 0; j < 8; j++)
            used_block += (blkmap[i] >> j) & 1;
    for(i = 0; i < IMAP_OFFSET; i++)
        for(j = 0; j < 8; j++)
            used_inodes += (inodemap[i] >> j) & 1;
    kprintf("Magic Number: 0x%x(KFS)\n", super_block->magic_num);
    kprintf("used block: %d/%d, start sector: %d (0x%x),\n", used_block, super_block->datablock_num, OFFSET_FS, OFFSET_FS);
    kprintf("block map offset: %d, occupied sector: %d\n", SD_BLKMAP_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE, BMAP_OFFSET/SECTOR_SIZE);
    kprintf("inode map offset: %d, occupied sector: %d, used: %d\n", SD_INODEMAP_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE, IMAP_OFFSET/SECTOR_SIZE, used_inodes);
    kprintf("inode offset: %d, occupied sector: %d\n", SD_INODE_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE, super_block->inodes_num);
    kprintf("data offset: %d, occupied sector: %d\n", SD_DATABLK_ADDR/SECTOR_SIZE-OFFSET_FS/SECTOR_SIZE, super_block->datablock_num);
    kprintf("inode entry size : 64B, dir entry size : 32B      \n");
}

int readdir(char *name)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    if(name[0] != '\0')
    {
        if(find_path(name)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }

    int j = 0;
    int k = 0;
    int dir_n = 0;
    sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(dir_n<current_dir_entry->son_num)
    {
        if(k == 128)
        {
            j ++;
            k = 0;
            sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        if(dbuf[k].name[0]!='\0') 
        {
            dir_n++;
            kprintf("%s\ti、 ref: %d\n",dbuf[k].name,inode[dbuf[k].inode_id].ref);
        }    
        k ++ ;
    }
    current_dir_entry = &inode[temp];
    return 1;
}
int cd(char *dir)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    if(dir[0] != '\0')
    {
        if(find_path(dir)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
        if(current_dir_entry->i_mode == 2)
        {
            kprintf("Open a Soft Link File\n");
            sdread(dbuf,current_dir_entry->direct[0],BLOCK_SIZE);
            current_dir_entry = &inode[dbuf[0].inode_id];
            if(current_dir_entry ->i_mode == 0)
            {
                kprintf("Path Not Found\n");
                current_dir_entry = &inode[temp];
                return 0;
            }
            return 1;
        }
    }
    return 1;
}


int mkdir(char *name)
{
    
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    char search_path[30];
    char last_path[30];
    get_search_path(search_path,name);
    get_last_path(last_path,name);
    if(search_path[0] != '\0')
    {
        if(find_path(search_path)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }

    
    int j = 0;
    int k = 0;
    int dir_n = 0;
    sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(dir_n<current_dir_entry->son_num)
    {
        if(k == 128)
        {
            j ++;
            k = 0;
            sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        if(dbuf[k].type==2 && !strcmp((char *)dbuf[k].name,last_path))
        {
            kprintf("Directory (%s) has already existed.\n",last_path);
            current_dir_entry = &inode[temp];
            return 0;
        }
        if(dbuf[k].type==5 && !strcmp((char *)dbuf[k].name,last_path))
        {
            kprintf("Soft Link File (%s) has already existed.\n",last_path);
            current_dir_entry = &inode[temp];
            return 0;
        }
        if(dbuf[k].name[0]!='\0') 
            dir_n++;
        k ++ ;
    }

    if(k == 128)
    {
        j ++;
        k = 0;
        sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    }
    strcpy((char *)dbuf[k].name,last_path);
    dbuf[k].type=2;
    dbuf[k].inode_id=alloc_free_inode();
    sdwrite((char *)dbuf,current_dir_entry->direct[j],BLOCK_SIZE);
    current_dir_entry->son_num++; 
    i_block_write(current_dir_entry->num);
    int i = dbuf[k].inode_id;
    inode[i].num=i;
    inode[i].i_mode=1;
    inode[i].file_mode=O_RDWR;
    inode[i].filesz = 0;
    inode[i].son_num=2;
    inode[i].ref = 2;
    current_dir_entry->ref ++;
    for(k = 0; k < 9; k++)
        inode[i].direct[k] = 0;
    inode[i].direct[0]=alloc_free_block();
    i_block_write(i);
    init_entry(inode[i].direct[0],inode[i].num,current_dir_entry->num);
    kprintf("A directory is created successfully! inode:%d addr:%x\n", inode[i].num,inode[i].direct[0]);
    current_dir_entry = &inode[temp];
    return 1;    
}

int rmdir(char *name)
{
    int temp = current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    char search_path[30];
    char last_path[30];
    get_search_path(search_path,name);
    get_last_path(last_path,name);
    if(search_path[0] != '\0')
    {
        if(find_path(search_path)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }

    int j = 0;
    int k = 0;
    int dir_n = 0;
    sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(dir_n<current_dir_entry->son_num)
    {
        if(k == 128)
        {
            j ++;
            k = 0;
            sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        if(dbuf[k].type==2 && !strcmp((char *)dbuf[k].name,last_path))
        {
            if(inode[dbuf[k].inode_id].son_num > 2)
            {
                kprintf("You can only delete an empty directory.\n");
                current_dir_entry = &inode[temp];
                return 0;
            }
            kprintf("A directory is deleted successfully! inode:%d name:%s\n", dbuf[k].inode_id,dbuf[k].name);
            dbuf[k].name[0] = '\0';
            dbuf[k].type = 0;
            int i = dbuf[k].inode_id;
            inode[i].num=-1;
            inode[i].i_mode=0;
            inode[i].file_mode=0;
            inode[i].filesz = 0;
            inode[i].son_num=0;
            inode[i].ref = 0;
            for(k = 0; k < 9; k++)
                inode[i].direct[k] = 0;
            i_block_write(i);
            free_inode(dbuf[k].inode_id);
            dbuf[k].inode_id = -1;
            sdwrite((char *)dbuf, current_dir_entry->direct[j], BLOCK_SIZE);      
            current_dir_entry->son_num--;
            i_block_write(current_dir_entry->num);
            current_dir_entry = &inode[temp];
            return 1;
        }
        if(dbuf[k].name[0]!='\0') 
            dir_n++;
        k ++ ;
    }
    kprintf("Directory (%s) not found.\n",last_path);
    current_dir_entry = &inode[temp];
    return 0;
}

 

int mknod(char *name)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    char search_path[30];
    char last_path[30];
    get_search_path(search_path,name);
    get_last_path(last_path,name);
    if(search_path[0] != '\0')
    {
        if(find_path(search_path)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }
    int j = 0;
    int k = 0;
    int dir_n = 0;
    sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(dir_n<current_dir_entry->son_num)
    {
        if(k == 128)
        {
            j ++;
            k = 0;
            sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        if(dbuf[k].type==1 && !strcmp((char *)dbuf[k].name,last_path))
        {
            kprintf("File (%s) has already existed.\n",last_path);
            current_dir_entry = &inode[temp];
            return 0;
        }
        if(dbuf[k].name[0]!='\0') 
            dir_n++;
        k ++ ;
    }

    if(k == 128)
    {
        j ++;
        k = 0;
        sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    }

    strcpy((char *)dbuf[k].name,last_path);
    dbuf[k].type = 1;
    dbuf[k].inode_id=alloc_free_inode();
    sdwrite((char *)dbuf,current_dir_entry->direct[j],BLOCK_SIZE);
     
    current_dir_entry->son_num++;
    i_block_write(current_dir_entry->num);

    int i = dbuf[k].inode_id;
    inode[i].num = i;
    inode[i].i_mode = 0;
    inode[i].file_mode=O_RDWR;
    inode[i].filesz = 0;
    inode[i].son_num = 1;
    inode[i].ref = 1;
    inode[i].direct[0]=alloc_free_block();
    for(j = 1; j < 9 ; j++)
        inode[i].direct[j] = 0;
    inode[i].indirect[0] = 0;
    inode[i].indirect[1] = 0;
    i_block_write(i);
    bzero(dbuf, sizeof(dbuf));
    sdwrite((char *)dbuf, inode[i].direct[0], BLOCK_SIZE);
    current_dir_entry = &inode[temp];
    return 1;    
}

int open(char *name, uint32_t access)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    if(name[0] != '\0')
    {
        if(find_path(name)==0 || current_dir_entry->i_mode == 1)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return -1;
        }
    }
    int fd_id= alloc_free_fd();
    fds[fd_id].inum = current_dir_entry->num;
    fds[fd_id].availability = access;
    fds[fd_id].cur_wpos = 0;
    fds[fd_id].cur_rpos = 0;
    current_dir_entry = &inode[temp];
    return fd_id;
}

int write(uint32_t fd, char *buff, uint32_t size)
{
    inode_entry_t *temp = &inode[fds[fd].inum];
    int size_temp = size;

    if(fds[fd].availability == O_RDONLY)
        return -1;

    uint32_t cur_pos =  fds[fd].cur_wpos;
    uint32_t start_pos =  fds[fd].cur_wpos;
    uint32_t start_id = cur_pos / BLOCK_SIZE;
    uint32_t end_id = (cur_pos+size) / BLOCK_SIZE;

    int i;
    for(i = start_id ;i <= end_id && i < 9; i ++)
    {
        if( i >= temp->son_num)
        {
            temp -> direct[i]=alloc_free_block();
            temp -> son_num ++;
            //i_block_write(temp->num);
        }
        if( cur_pos % BLOCK_SIZE + size_temp <= BLOCK_SIZE)
        {
            sdread((char *)fbuf, temp -> direct[i], BLOCK_SIZE);
            memcpy(fbuf + cur_pos % BLOCK_SIZE, buff + cur_pos-start_pos, size_temp);
            cur_pos += size_temp;
            sdwrite((char *)fbuf, temp -> direct[i], BLOCK_SIZE);
            size_temp = 0;
        }
        else
        {
            sdread((char *)fbuf,temp -> direct[i], BLOCK_SIZE);
            memcpy(fbuf + cur_pos % BLOCK_SIZE, buff + (cur_pos-start_pos), BLOCK_SIZE - cur_pos % BLOCK_SIZE);
            cur_pos += BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            size_temp -= BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            sdwrite((char *)fbuf, temp -> direct[i], BLOCK_SIZE);  
        }
    }
    if(end_id >= 9)
    {
        if(temp->indirect[0] == 0)
        {
            temp->indirect[0] = alloc_free_block();
            //i_block_write(temp->num);
            kprintf("Start Using indirect 1\n");
        }
        for(;i <= end_id && i < 1033; i ++)
        {
        if( i >= temp->son_num)
        {
            sdread((char *)addr_buf1,temp->indirect[0],BLOCK_SIZE);
            addr_buf1[i-9] = alloc_free_block();
            sdwrite((char *)addr_buf1,temp->indirect[0],BLOCK_SIZE);
            temp -> son_num ++;
            //i_block_write(temp->num);
        }
        if( cur_pos % BLOCK_SIZE + size_temp <= BLOCK_SIZE)
        {
            //sdread((char *)addr_buf1,temp->indirect[0],BLOCK_SIZE);
            sdread((char *)fbuf, addr_buf1[i-9], BLOCK_SIZE);
            memcpy(fbuf + cur_pos % BLOCK_SIZE, buff + cur_pos-start_pos, size_temp);
            cur_pos += size_temp;
            sdwrite((char *)fbuf, addr_buf1[i-9], BLOCK_SIZE);
            size_temp = 0;
        }
        else
        {
            //sdread((char *)addr_buf1,temp->indirect[0],BLOCK_SIZE);
            sdread((char *)fbuf,addr_buf1[i-9], BLOCK_SIZE);
            memcpy(fbuf + cur_pos % BLOCK_SIZE, buff + (cur_pos-start_pos), BLOCK_SIZE - cur_pos % BLOCK_SIZE);
            cur_pos += BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            size_temp -= BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            sdwrite((char *)fbuf, addr_buf1[i-9], BLOCK_SIZE);  
        }
        }
    }
    if(end_id >= 1033)
    {
        if(temp->indirect[1] == 0)
        {
            temp -> indirect[1]=alloc_free_block();
            //i_block_write(temp->num);
            kprintf("Start Using indirect 2\n");
        }
        for(;i <= end_id && i < 2057; i ++)
        {
        if( i >= temp->son_num)
        {
            sdread((char *)addr_buf2,temp->indirect[1],BLOCK_SIZE);
            addr_buf2[i-1033] = alloc_free_block();
            sdwrite((char *)addr_buf2,temp->indirect[1],BLOCK_SIZE);
            temp -> son_num ++;
            i_block_write(temp->num);
        }
        if( cur_pos % BLOCK_SIZE + size_temp <= BLOCK_SIZE)
        {
            //sdread((char *)addr_buf2,temp->indirect[1],BLOCK_SIZE);
            sdread((char *)fbuf, addr_buf2[i-1033], BLOCK_SIZE);
            memcpy(fbuf + cur_pos % BLOCK_SIZE, buff + cur_pos-start_pos, size_temp);
            cur_pos += size_temp;
            sdwrite((char *)fbuf, addr_buf2[i-1033], BLOCK_SIZE);
            size_temp = 0;
        }
        else
        {
            //sdread((char *)addr_buf2,temp->indirect[1],BLOCK_SIZE);
            sdread((char *)fbuf,addr_buf2[i-1033], BLOCK_SIZE);
            memcpy(fbuf + cur_pos % BLOCK_SIZE, buff + (cur_pos-start_pos), BLOCK_SIZE - cur_pos % BLOCK_SIZE);
            cur_pos += BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            size_temp -= BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            sdwrite((char *)fbuf, addr_buf2[i-9], BLOCK_SIZE);  
        }
        }
    }
    temp->filesz+=size;
    i_block_write(temp->num);
    fds[fd].cur_wpos=cur_pos;
    return size;
}

int read(uint32_t fd, char *buff, uint32_t size)
{
    inode_entry_t *temp = &inode[fds[fd].inum];
    int size_temp = size;

    if(fds[fd].availability == O_WRONLY)
        return -1;

    uint32_t cur_pos =  fds[fd].cur_rpos;
    uint32_t start_pos =  fds[fd].cur_rpos;
    uint32_t start_id = cur_pos / BLOCK_SIZE;
    uint32_t end_id = (cur_pos+size) / BLOCK_SIZE;

    int i;
    for(i = start_id ;i <= end_id && i < 9; i ++)
    {
        if( i >= temp->son_num)
        {
            kprintf("Read too much!\n");
            return -1;
        }
        if( cur_pos % BLOCK_SIZE + size_temp <= BLOCK_SIZE)
        {
            sdread((char *)fbuf, temp -> direct[i], BLOCK_SIZE);
            memcpy( buff + cur_pos-start_pos,fbuf + cur_pos % BLOCK_SIZE, size_temp);
            cur_pos += size_temp;
            size_temp = 0;
        }
        else
        {
            sdread((char *)fbuf,temp -> direct[i], BLOCK_SIZE);
            memcpy( buff + (cur_pos-start_pos), fbuf + cur_pos % BLOCK_SIZE,BLOCK_SIZE - cur_pos % BLOCK_SIZE);
            cur_pos += BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            size_temp -= BLOCK_SIZE - cur_pos % BLOCK_SIZE;
        }
    }
    if(end_id >= 9)
    {
        for(;i <= end_id && i < 1033; i ++)
        {
        if( i >= temp->son_num)
        {
            kprintf("Read too much!\n");
            return -1;
        }
        if( cur_pos % BLOCK_SIZE + size_temp <= BLOCK_SIZE)
        {
            sdread((char *)addr_buf1,temp->indirect[0],BLOCK_SIZE);
            sdread((char *)fbuf, addr_buf1[i-9], BLOCK_SIZE);
            memcpy( buff + cur_pos-start_pos,fbuf + cur_pos % BLOCK_SIZE, size_temp);
            cur_pos += size_temp;
            size_temp = 0;
        }
        else
        {
            sdread((char *)addr_buf1,temp->indirect[0],BLOCK_SIZE);
            sdread((char *)fbuf,addr_buf1[i-9], BLOCK_SIZE);
            memcpy( buff + (cur_pos-start_pos), fbuf + cur_pos % BLOCK_SIZE,BLOCK_SIZE - cur_pos % BLOCK_SIZE);
            cur_pos += BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            size_temp -= BLOCK_SIZE - cur_pos % BLOCK_SIZE; 
        }
        }
    }
    if(end_id >= 1033)
    {
        for(;i <= end_id && i < 2057; i ++)
        {
        if( i >= temp->son_num)
        {
            kprintf("Read too much!\n");
            return -1;
        }
        if( cur_pos % BLOCK_SIZE + size_temp <= BLOCK_SIZE)
        {
            sdread((char *)addr_buf2,temp->indirect[1],BLOCK_SIZE);
            sdread((char *)fbuf, addr_buf2[i-1033], BLOCK_SIZE);
            memcpy( buff + cur_pos-start_pos,fbuf + cur_pos % BLOCK_SIZE, size_temp);
            cur_pos += size_temp;
            size_temp = 0;
        }
        else
        {
            sdread((char *)addr_buf2,temp->indirect[1],BLOCK_SIZE);
            sdread((char *)fbuf,addr_buf2[i-1033], BLOCK_SIZE);
            memcpy( buff + (cur_pos-start_pos), fbuf + cur_pos % BLOCK_SIZE,BLOCK_SIZE - cur_pos % BLOCK_SIZE);
            cur_pos += BLOCK_SIZE - cur_pos % BLOCK_SIZE;
            size_temp -= BLOCK_SIZE - cur_pos % BLOCK_SIZE;
        }
        }
    }

    fds[fd].cur_rpos=cur_pos;
    return size;
}

int close(uint32_t fd)
{
    bzero((char *)&fds[fd], sizeof(fds[fd]));
    return 0;
}

 
int cat(char *name)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    if(name[0] != '\0')
    {
        if(find_path(name)==0 || current_dir_entry->i_mode == 1)
        {
        kprintf("Path Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
        if(current_dir_entry->i_mode == 2)
        {
            kprintf("Open a Soft Link File\n");
            sdread(dbuf,current_dir_entry->direct[0],BLOCK_SIZE);
            current_dir_entry = &inode[dbuf[0].inode_id];
            if(current_dir_entry->i_mode == 1)
            {
                kprintf("Path Not Found\n");
                current_dir_entry = &inode[temp];
                return 0;
            }
        }
    }

    int j = 0;
    int k = 0;
    int char_n = 0;
    sdread(fbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(char_n<current_dir_entry->filesz && char_n < 0x9000)
    {
        if(k == 0x1000)
        {
            j ++;
            k = 0;
            sdread(fbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        char_n++;
        kprintf("%c",fbuf[k]); 
        k ++ ;
    }
    if(current_dir_entry->filesz > 0x9000)//support large file
    {
        sdread((char *)addr_buf1,current_dir_entry->indirect[0],BLOCK_SIZE);
        sdread((char *)addr_buf2,current_dir_entry->indirect[1],BLOCK_SIZE);
        j = 0;
        k = 0;
        sdread((char *)fbuf,addr_buf1[0],BLOCK_SIZE);
        while(char_n<current_dir_entry->filesz && char_n < 0x409000)
        {
            if(k == 0x1000)
            {
                j ++;
                k = 0;
                sdread(fbuf, addr_buf1[j],BLOCK_SIZE);
            }
            char_n++;
            kprintf("%c",fbuf[k]); 
            k ++ ;
        }
        k = 0;
        j = 0;
        sdread((char *)fbuf,addr_buf2[0],BLOCK_SIZE);
        while(char_n<current_dir_entry->filesz)
        {
            if(k == 0x1000)
            {
                j ++;
                k = 0;
                sdread(fbuf, addr_buf2[j],BLOCK_SIZE);
            }
            char_n++;
            kprintf("%c",fbuf[k]); 
            k ++ ;
        }
    }
    current_dir_entry = &inode[temp];
}
int hard_link(char *oldfile, char *newfile)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    char search_path[30];
    char last_path[30];
    get_search_path(search_path,newfile);
    get_last_path(last_path,newfile);
    if(oldfile[0] != '\0')
    {
        int flag = find_path(oldfile);
        if(flag==0)
        {
        kprintf("Path of Old File Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
        else if ((flag == 1) && current_dir_entry -> i_mode == 1)
        {
            kprintf("Building hard link for directory is not permitted\n");
            current_dir_entry = &inode[temp];
            return 0;
        }
    }

    int old_file_inum = current_dir_entry->num;
    current_dir_entry = &inode[temp];

    if(search_path[0] != '\0')
    {
        if(find_path(search_path)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path of New File Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }

    int j = 0;
    int k = 0;
    int dir_n = 0;
    sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(dir_n<current_dir_entry->son_num)
    {
        if(k == 128)
        {
            j ++;
            k = 0;
            sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        if(dbuf[k].type==1 && !strcmp((char *)dbuf[k].name,last_path))
        {
            kprintf("File (%s) has already existed.\n",last_path);
            current_dir_entry = &inode[temp];
            return 0;
        }
        if(dbuf[k].name[0]!='\0') 
            dir_n++;
        k ++ ;
    }

    if(k == 128)
    {
        j ++;
        k = 0;
        sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    }

    strcpy((char *)dbuf[k].name,last_path);
    dbuf[k].type = 1;
    dbuf[k].inode_id = old_file_inum;
    sdwrite((char *)dbuf,current_dir_entry->direct[j],BLOCK_SIZE);
    inode[old_file_inum].ref ++;
    current_dir_entry->son_num++;
    i_block_write(current_dir_entry->num);

    current_dir_entry = &inode[temp];
    return 1;    
}

int soft_link(char *oldfile, char *newfile)
{
    int temp=current_dir_entry->num;
    if(super_block->magic_num != KFS_MAGIC)
    {
        kprintf("No File System exists.\n");
        return -1;
    }
    char search_path[30];
    char last_path[30];
    get_search_path(search_path,newfile);
    get_last_path(last_path,newfile);
    if(oldfile[0] != '\0')
    {
        int flag = find_path(oldfile);
        if(flag==0)
        {
        kprintf("Path of Old File Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }

    int old_file_inum = current_dir_entry->num;
    current_dir_entry = &inode[temp];

    if(search_path[0] != '\0')
    {
        if(find_path(search_path)==0 || current_dir_entry->i_mode == 0)
        {
        kprintf("Path of New File Not Found\n");
        current_dir_entry = &inode[temp];
        return 0;
        }
    }

    int j = 0;
    int k = 0;
    int dir_n = 0;
    sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    while(dir_n<current_dir_entry->son_num)
    {
        if(k == 128)
        {
            j ++;
            k = 0;
            sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
        }
        if(dbuf[k].type==5 && !strcmp((char *)dbuf[k].name,last_path))
        {
            kprintf("Soft Link File (%s) has already existed.\n",last_path);
            current_dir_entry = &inode[temp];
            return 0;
        }
        if(dbuf[k].name[0]!='\0') 
            dir_n++;
        k ++ ;
    }

    if(k == 128)
    {
        j ++;
        k = 0;
        sdread((char *)dbuf, current_dir_entry -> direct[j],BLOCK_SIZE);
    }

    strcpy((char *)dbuf[k].name,last_path);
    dbuf[k].type = 5;
    dbuf[k].inode_id = alloc_free_inode();
    sdwrite((char *)dbuf,current_dir_entry->direct[j],BLOCK_SIZE);
     
    current_dir_entry->son_num++;
    i_block_write(current_dir_entry->num);

    int i = dbuf[k].inode_id;
    inode[i].num = i;
    inode[i].i_mode = 2;
    inode[i].file_mode=O_RDWR;
    inode[i].filesz = strlen(oldfile);
    inode[i].son_num = 1;
    inode[i].direct[0]=alloc_free_block();
    for(j = 1; j < 9 ; j++)
        inode[i].direct[j] = 0;
    inode[i].indirect[0] = 0;
    inode[i].indirect[1] = 0;
    i_block_write(i);
    bzero(dbuf, sizeof(dbuf));
    dbuf[0].inode_id = old_file_inum;
    sdwrite((char *)dbuf, inode[i].direct[0], BLOCK_SIZE);
    current_dir_entry = &inode[temp];
    return 1;    
}

 
void init_fs()
{
    bzero((void *)zero, BLOCK_SIZE);

    super_block = (super_block_t *)SUPERBLK_ADDR;
	sdread((char *)super_block,OFFSET_FS,SUPERBLK_OFFSET);
	if(super_block->magic_num != KFS_MAGIC)
		do_mkfs();
    //
    blkmap = (uint8_t *)BLKMAP_ADDR;
    sdread((char *)(blkmap), (uint32_t)SD_BLKMAP_ADDR, BMAP_OFFSET);

    inodemap = (uint8_t *)INODEMAP_ADDR;
    sdread((char *)(inodemap), (uint32_t)SD_INODEMAP_ADDR, IMAP_OFFSET);

    inode = (inode_entry_t *)INODE_ADDR;
    sdread((char *)(inode), (uint32_t)SD_INODE_ADDR, 0x2000);
    bzero((char *)fds, sizeof(fds));
    current_dir_entry = inode;
}

